"""
用户认证和授权模块
提供用户登录、注册、权限管理功能
"""
from flask import Blueprint, render_template, request, redirect, url_for, flash, jsonify
from flask_login import login_user, logout_user, login_required, current_user
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, SubmitField
from wtforms.validators import DataRequired, Email, Length, EqualTo, ValidationError
from datetime import datetime
import sys
import os

# 添加项目根目录到路径
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))))

from services.shared.models import db
from services.shared.models.user import User
from services.shared import SystemLog, create_response
from services.shared.log_utils import log_user_action, log_security_event, log_login_attempt
from services.shared.logger import get_logger

# 初始化日志记录器
logger = get_logger('web.auth')

# 创建蓝图
auth_bp = Blueprint('auth', __name__)


class LoginForm(FlaskForm):
    """登录表单"""
    username = StringField('用户名', validators=[DataRequired(), Length(1, 64)])
    password = PasswordField('密码', validators=[DataRequired()])
    remember_me = BooleanField('记住我')
    submit = SubmitField('登录')


class RegistrationForm(FlaskForm):
    """注册表单"""
    username = StringField('用户名', validators=[
        DataRequired(), Length(1, 64)])
    email = StringField('邮箱', validators=[DataRequired(), Email()])
    password = PasswordField('密码', validators=[
        DataRequired(), Length(6, 128)])
    password2 = PasswordField('确认密码', validators=[
        DataRequired(), EqualTo('password', message='密码必须匹配')])
    submit = SubmitField('注册')
    
    def validate_username(self, field):
        if User.query.filter_by(username=field.data).first():
            raise ValidationError('用户名已存在')
    
    def validate_email(self, field):
        if User.query.filter_by(email=field.data).first():
            raise ValidationError('邮箱已被注册')


class ChangePasswordForm(FlaskForm):
    """修改密码表单"""
    old_password = PasswordField('原密码', validators=[DataRequired()])
    password = PasswordField('新密码', validators=[
        DataRequired(), Length(6, 128)])
    password2 = PasswordField('确认新密码', validators=[
        DataRequired(), EqualTo('password', message='密码必须匹配')])
    submit = SubmitField('修改密码')


@auth_bp.route('/login', methods=['GET', 'POST'])
def login():
    """用户登录"""
    logger.info(f"用户登录请求 - IP: {request.remote_addr}, User-Agent: {request.headers.get('User-Agent', 'Unknown')}")
    
    if current_user.is_authenticated:
        logger.info(f"用户 {current_user.username} 已登录，重定向到仪表板")
        return redirect(url_for('dashboard.overview'))
    
    form = LoginForm()
    if form.validate_on_submit():
        username = form.username.data
        logger.info(f"用户 {username} 尝试登录")
        
        user = User.query.filter_by(username=username).first()
        
        if user and user.check_password(form.password.data):
            if not user.is_active:
                logger.warning(f"用户 {username} 登录失败 - 账户已被禁用")
                flash('账户已被禁用，请联系管理员', 'error')
                return render_template('auth/login.html', form=form)
            
            # 登录用户
            login_user(user, remember=form.remember_me.data)
            logger.info(f"用户 {username} 登录成功 - 记住我: {form.remember_me.data}")
            
            # 更新最后登录时间
            user.last_login = datetime.utcnow()
            db.session.commit()
            logger.debug(f"更新用户 {username} 最后登录时间")
            
            # 为用户创建脚本目录（如果不存在）
            try:
                from services.shared.file_utils import create_user_script_directory
                create_user_script_directory(user.username)
                logger.debug(f"为用户 {username} 创建脚本目录")
            except Exception as e:
                logger.error(f"为用户 {username} 创建脚本目录失败: {str(e)}")
                # 不影响登录，仅记录错误
            
            # 记录登录日志
            log_login_attempt(
                username=user.username,
                success=True,
                ip_address=request.remote_addr,
                user_agent=request.headers.get('User-Agent')
            )
            
            flash('登录成功！', 'success')
            
            # 重定向到原页面或首页
            next_page = request.args.get('next')
            if not next_page or not next_page.startswith('/'):
                next_page = url_for('dashboard.overview')
            logger.info(f"用户 {username} 登录后重定向到: {next_page}")
            return redirect(next_page)
        else:
            # 记录登录失败
            logger.warning(f"用户 {username} 登录失败 - 用户名或密码错误")
            log_login_attempt(
                username=form.username.data,
                success=False,
                ip_address=request.remote_addr,
                user_agent=request.headers.get('User-Agent')
            )
            flash('用户名或密码错误', 'error')
    
    return render_template('auth/login.html', form=form)


@auth_bp.route('/logout')
@login_required
def logout():
    """用户登出"""
    username = current_user.username
    user_id = current_user.id
    
    logger.info(f"用户 {username} 请求登出 - IP: {request.remote_addr}")
    
    # 记录登出日志
    log_user_action('INFO', 'auth', f'用户 {username} 登出', user_id=user_id)
    
    logout_user()
    logger.info(f"用户 {username} 已成功登出")
    flash('已成功登出', 'info')
    return redirect(url_for('auth.login'))


@auth_bp.route('/register', methods=['GET', 'POST'])
def register():
    """用户注册 - 已禁用，请联系管理员"""
    logger.warning(f"用户尝试访问已禁用的注册页面 - IP: {request.remote_addr}")
    flash('注册功能已禁用，请联系管理员添加用户账户', 'warning')
    return redirect(url_for('auth.login'))


@auth_bp.route('/change_password', methods=['GET', 'POST'])
@login_required
def change_password():
    """修改密码"""
    username = current_user.username
    logger.info(f"用户 {username} 访问修改密码页面")
    
    form = ChangePasswordForm()
    if form.validate_on_submit():
        logger.info(f"用户 {username} 提交密码修改请求")
        
        if current_user.check_password(form.old_password.data):
            current_user.set_password(form.password.data)
            db.session.commit()
            
            # 记录密码修改日志
            log_user_action('INFO', 'auth', f'用户 {username} 修改密码', user_id=current_user.id)
            logger.info(f"用户 {username} 密码修改成功")
            
            flash('密码修改成功！', 'success')
            return redirect(url_for('dashboard.overview'))
        else:
            logger.warning(f"用户 {username} 密码修改失败 - 原密码错误")
            flash('原密码错误', 'error')
    
    return render_template('auth/change_password.html', form=form)


@auth_bp.route('/profile')
@login_required
def profile():
    """用户档案"""
    logger.info(f"用户 {current_user.username} 访问个人档案页面")
    return render_template('auth/profile.html', user=current_user)


# API 接口
@auth_bp.route('/api/login', methods=['POST'])
def api_login():
    """API登录接口"""
    logger.info(f"API登录请求 - IP: {request.remote_addr}, User-Agent: {request.headers.get('User-Agent', 'Unknown')}")
    
    data = request.get_json()
    
    if not data or not data.get('username') or not data.get('password'):
        logger.warning(f"API登录请求缺少必要参数 - IP: {request.remote_addr}")
        return jsonify(create_response(False, '用户名和密码不能为空')), 400
    
    username = data['username']
    logger.info(f"用户 {username} 通过API尝试登录")
    
    user = User.query.filter_by(username=username).first()
    
    if user and user.check_password(data['password']):
        if not user.is_active:
            logger.warning(f"用户 {username} API登录失败 - 账户已被禁用")
            return jsonify(create_response(False, '账户已被禁用')), 403
        
        # 登录用户
        remember_me = data.get('remember_me', False)
        login_user(user, remember=remember_me)
        logger.info(f"用户 {username} API登录成功 - 记住我: {remember_me}")
        
        # 更新最后登录时间
        user.last_login = datetime.utcnow()
        db.session.commit()
        logger.debug(f"更新用户 {username} 最后登录时间")
        
        # 为用户创建脚本目录（如果不存在）
        try:
            from services.shared.file_utils import create_user_script_directory
            create_user_script_directory(user.username)
            logger.debug(f"为用户 {username} 创建脚本目录")
        except Exception as e:
            logger.error(f"为用户 {username} 创建脚本目录失败: {str(e)}")
            # 不影响登录，仅记录错误
        
        # 记录登录日志
        log_login_attempt(
            username=user.username,
            success=True,
            ip_address=request.remote_addr,
            user_agent=request.headers.get('User-Agent')
        )
        
        logger.info(f"用户 {username} API登录完成，返回用户信息")
        return jsonify(create_response(True, '登录成功', user.to_dict()))
    
    # 记录API登录失败
    logger.warning(f"用户 {username} API登录失败 - 用户名或密码错误")
    log_login_attempt(
        username=data.get('username', 'unknown'),
        success=False,
        ip_address=request.remote_addr,
        user_agent=request.headers.get('User-Agent')
    )
    return jsonify(create_response(False, '用户名或密码错误')), 401


@auth_bp.route('/api/user_info')
@login_required
def api_user_info():
    """获取当前用户信息"""
    logger.info(f"用户 {current_user.username} 请求获取用户信息 - IP: {request.remote_addr}")
    return jsonify(create_response(True, '获取成功', current_user.to_dict()))


def admin_required(f):
    """管理员权限装饰器"""
    def decorated_function(*args, **kwargs):
        if not current_user.is_authenticated:
            logger.warning(f"未认证用户尝试访问管理员功能 - IP: {request.remote_addr}")
            return redirect(url_for('auth.login'))
        if not current_user.is_admin:
            logger.warning(f"非管理员用户 {current_user.username} 尝试访问管理员功能 - IP: {request.remote_addr}")
            log_security_event(
                event_type='unauthorized_access',
                description=f'用户 {current_user.username} 尝试访问管理员功能',
                user_id=current_user.id,
                ip_address=request.remote_addr
            )
            flash('需要管理员权限', 'error')
            return redirect(url_for('dashboard.overview'))
        
        logger.debug(f"管理员用户 {current_user.username} 访问管理员功能: {f.__name__}")
        return f(*args, **kwargs)
    decorated_function.__name__ = f.__name__
    return decorated_function